﻿
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.Extensions.DependencyInjection;
using RBACProject.Domian;
using RBACProject.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security.Claims;

namespace RBACProject.WebApi.Filter
{
    /// <summary>
    /// 创建授权过滤器
    /// </summary>  
    //指定特性可应用于类和方法  特性可以在同一个目标上多次使用  特性是否可以从基类继承到派生类  如果设置为 true，则派生类将继承基类上的特性。如果设置为 false，则特性不会继承给派生类。
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
    public class AuthorizationFilterAttribute : AuthorizeAttribute, IAuthorizationFilter
    { 
        //思路
        //1.首先，判断是否使用了[AllowAnonymous] 特性，如果是则不进行授权验证。
        //2.获取当前请求的路由信息和控制器、动作名称。
        //3.判断用户是否已登录，如果未登录则返回 401 未授权状态码。
        //4.如果已登录，则根据路由信息获取菜单对应的角色列表，并获取当前登录用户所拥有的角色。
        //5.判断当前登录用户的角色是否有权限访问该菜单，如果没有则返回 403 禁止访问状态码

        /// <summary>
        ///  自定义授权过滤器的具体实现
        /// </summary>
        /// <param name="context"></param>
        /// <exception cref="System.NotImplementedException"></exception>
        public void OnAuthorization(AuthorizationFilterContext context) 
        {
            //Any() 是 LINQ 中的一个扩展方法，用于判断集合是否包含任何元素。它返回一个布尔值，指示集合中是否存在满足给定条件的元素。
            //1.无参数的 Any()：用于检查集合是否包含任何元素。如果集合非空，则返回 true；如果集合为空，则返回 false。
            //2.带有条件的 Any(predicate)：用于检查集合中是否存在满足给定条件的元素。predicate 是一个委托或 Lambda 表达式，用于定义元素应满足的条件

            // 排除使用了 [AllowAnonymous] 特性接口（方法一）
            if (context.ActionDescriptor.EndpointMetadata.Any(m=>m is Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute))
            {
                return;
            }
            //获取信息
            string attributRouteInfo = context.ActionDescriptor.AttributeRouteInfo.Template; // 路由信息
            ControllerActionDescriptor descriptor = context.ActionDescriptor as ControllerActionDescriptor;
            string controllerName = descriptor.ControllerName; // 控制器名称
            string actionName = descriptor.ActionName;  // 动作名称
            //IsAuthenticated 是否已通过身份验证
            bool isLogin = context.HttpContext.User.Identity.IsAuthenticated;   // 判断用户是否登录
            if (!isLogin)
            {
                // 未登录返回 401 状态码（未授权）
                context.Result = new StatusCodeResult((int)HttpStatusCode.Unauthorized);
            }
            else {
                // 排除登录接口的权限锁（方法二）
                if (!attributRouteInfo.Contains("/api/Menu/GetMenuRoute"))
                {
                    return;
                }
                // 获取服务提供程序
                IServiceProvider serviceProvider = context.HttpContext.RequestServices;
                var menuRepositroy = serviceProvider.GetService<IBaseRepository<MenuModel>>();
                var rolemenuRepositroy = serviceProvider.GetService<IBaseRepository<RoleMenuModel>>();

                //查询
                var menuRoleIds = rolemenuRepositroy.QueryAsnycAll().Where(m => menuRepositroy.QueryAsnycAll()
                    .Where(menu => menu.MenuApi == attributRouteInfo)//AttributRouteInfo
                    .Select(s => s.MenuId).Contains(m.MenuId))
                    .Select(x => x.RoleId).ToList();
                //获取当前登录的用户名
                string UserName = context.HttpContext.User.Identity.Name;
                IEnumerable<Claim> claims = context.HttpContext.User.Claims;
                //获取当前登录角色ID
                int[] roleid = claims.First(x => x.Type == ClaimTypes.Role).Value.Split(',')
                    .Select(m => Convert.ToInt32(m)).ToArray();
                //判断当前用户角色是否有访问菜单的权限
                if (!roleid.Any(a => menuRoleIds.Contains(a)))
                {
                    //如果没有则返回 403 禁止访问状态码
                    context.Result = new StatusCodeResult((int)HttpStatusCode.Forbidden);
                }
            }

        }



    }
}
